﻿'Description:预有限排产-有限产能排产类
'Copyright (c) : 通力凯顿（北京）系统集成有限公司
'Writer:Wangjh 
'create Date:20230315
'Rewriter:
'Rewrite Date:
Imports BusinessRule.DTO
Imports DatabaseAccess.DBUtility


Public Class TryLimitedCapacityBusiness
    Inherits LimitedScheduleBaseBusiness
    Private m_containerObj As New uLMSContainerBusiness

    Protected Overrides Property Sql_ConApsSelect As String = <string> select * from ApsPlanInfopre ai where ai.containerid='{0}' and ai.workflowid='{1}' and ai.ApsType2=2 and ai.ApsType='{2}' and ai.Isuse=0 </string>.Value
    Protected Overrides Property Sql_ConApsInsert As String = <STIRNG> insert into ApsPlanInfopre ai (ai.ApsType2,ai.apsplaninfoid,ai.apstype,ai.containerid,ai.createdate,ai.createemployeeid,ai.endtime,ai.isuse,
 ai.lastchangedate,ai.notes,ai.starttime,ai.workflowid) values (2,'{0}','{1}','{2}',sysdate,'{3}',to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,
 SYSDATE,'{5}',to_date('{6}','YYYY-MM-DD HH24:MI:SS'),'{7}') </STIRNG>.Value
    Protected Overrides Property Sql_ConApsUpdate As String = <string>update ApsPlanInfopre ai set ai.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS') , ai.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'), 
 ai.lastchangedate=sysdate where ai.apsplaninfoid='{2}'</string>.Value

    ''' <summary>
    ''' 工序级排产信息查询sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsSelect As String = <string>select * from ApsPlanDetailpre ad where ad.apsplaninfoid='{0}' and ad.ContainerID='{1}' and ad.isuse=0 </string>.Value

    ''' <summary>
    ''' 工序级排产信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsInsert As String = <string> insert into ApsPlanDetailpre ad (ad.CreateDate,ad.LastChangeDate,ad.apsplandetailid,ad.apsplaninfoid,ad.containerid,ad.starttime,ad.endtime,ad.isuse,
ad.workflowstepid,ad.workflowstepname,ad.notes,ad.workcenterid,ad.teamid,ad.resourceid,CreateEmployeeID,ad.SpecFactoryID,ad.Resstarttime,ad.Resendtime,ad.qty,ad.status) values (sysdate,sysdate,'{0}','{1}','{2}',to_date('{3}','YYYY-MM-DD HH24:MI:SS'),to_date('{4}','YYYY-MM-DD HH24:MI:SS'),0,'{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}',to_date('{13}','YYYY-MM-DD HH24:MI:SS'),to_date('{14}','YYYY-MM-DD HH24:MI:SS'),{15},0) </string>.Value
    ''' <summary>
    ''' 工序级排产信息更新sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsUpdate As String = <string> update ApsPlanDetailpre ad set  AD.Status=0,ad.FeedbackDate=null,ad.ConfirmDate=null,ad.ConfirmEmpID=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
ad.workcenterid='{2}',ad.teamid='{3}',ad.resourceid='{4}',ad.SpecFactoryID='{5}',ad.Resstarttime=to_date('{6}','YYYY-MM-DD HH24:MI:SS'),ad.Resendtime=to_date('{7}','YYYY-MM-DD HH24:MI:SS'),ad.qty={8} where ad.apsplaninfoid='{9}' and ad.workflowstepid='{10}' and apsplandetailid='{11}' </string>.Value
    ''' <summary>
    ''' 工序级排产信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsDel As String = <string>delete ApsPlanDetailpre ae where ae.apsplandetailid='{0}'</string>.Value

    ''' <summary>
    ''' 工序级排产信息范围计算sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Property Sql_StepApsRange As String = <string>select ad.containerid,min(ad.starttime) minstarttime,max(ad.endtime) maxendtime from ApsPlanDetailpre ad where ad.apsplaninfoid='{0}' and ad.isuse=0 group by ad.containerid </string>.Value


    ''' <summary>
    ''' 工序级排产人员信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_StepEmpApsDel As String = <string>delete ApsPlanDetailemp ae where ae.apsplandetailid='{0}'</string>.Value
    ''' <summary>
    ''' 工序级排产人员信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_StepEmpApsInsert As String = <string>insert into ApsPlanDetailemp (Apsplandetailid,Employeeid,Isuse,Apsplandetailempid) values ('{0}','{1}',0,'{2}')</string>.Value

    ''' <summary>
    ''' 工序级排产人员时间信息删除sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_EmpTimeApsDel As String = <string>delete from apsplandetailemptime where apsplandetailempid in (select apsplandetailempid from apsplandetailemp where apsplandetailid='{0}') </string>.Value
    ''' <summary>
    ''' 工序级排产人员时间信息插入sql
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Property Sql_EmpTimeApsInsert As String = <string>insert into apsplandetailemptime  (apsplandetailempid, apsplandetailemptimeid,starttime, isuse, endtime, employeeid) values ('{0}', '{1}', to_date('{2}','yyyy/mm/dd hh24:mi:ss'), '0', to_date('{3}','yyyy/mm/dd hh24:mi:ss'),'{4}')</string>.Value


    '-------------------------重写的方法

    ''' <summary>
    ''' 获取设备占用信息，指定时间之后
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIdList"></param>
    ''' <param name="startTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Function GetResOccupyDtAfterTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal startTime As String, ByVal apsType As String, Optional ByVal apsTempTable As String = "") As DataTable
        Dim resOccupyAps = "select ad.* from ApsPlanDetailpre ad where ad.ResourceID in ({0}) and ad.isuse=0 and ad.Status=1 " +
           "and ( ad.Resstarttime>= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           "or ( " +
           "ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
           ") ) and not exists (select 1 from containersteprecord csr where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid " +
           "and csr.usecompleted=1) " +
            " union all " +
            " select ad.* from ApsPlanDetailpre ad " +
            " left join ApsPlanInfopre ap on ap.apsplaninfoid=ad.apsplaninfoid " +
            " where ap.apstype2=2 and ad.status=0 and ap.apstype='{3}' and ap.containerid in ({2}) and ad.ResourceID in ({0}) "
        Return DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, resIds, startTime, "'" + String.Join(",", conIdList).Replace(",", "','") + "'", apsType))
    End Function

    ''' <summary>
    ''' 获取设备占用信息，指定时间之前
    ''' </summary>
    ''' <param name="resIds"></param>
    ''' <param name="conIdList"></param>
    ''' <param name="endTime"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Overrides Function GetResOccupyDtBeforeTime(ByVal resIds As String, ByVal conIdList As List(Of String), ByVal endTime As String, ByVal apsType As String) As DataTable
        Dim resOccupyAps = "select ad.* from ApsPlanDetailpre ad where ad.ResourceID in ({0}) and ad.isuse=0 and ad.Status=1 " +
"and ( ad.Resendtime<= to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
"or ( " +
"ad.Resstarttime<to_date('{1}','yyyy-mm-dd hh24:mi:ss') and ad.Resendtime>to_date('{1}','yyyy-mm-dd hh24:mi:ss') " +
") ) and not exists (select 1 from containersteprecord csr where csr.containerid=ad.containerid and csr.workflowstepid=ad.workflowstepid " +
"and csr.usecompleted=1)" +
" union all " +
            " select ad.* from ApsPlanDetailpre ad " +
            " left join ApsPlanInfopre ap on ap.apsplaninfoid=ad.apsplaninfoid " +
            " where ap.apstype2=2 and ad.status=0 and ap.apstype='{3}' and ap.containerid in ({2}) and ad.ResourceID in ({0})  "
        Return DatabaseHelper.Query_ReturnDatTable(String.Format(resOccupyAps, resIds, endTime, "'" + String.Join(",", conIdList).Replace(",", "','") + "'", apsType))
    End Function

    ''' <summary>
    ''' 子卡排产的开始结束时间，取子卡对应主卡子工序的排产开始结束时间
    ''' </summary>
    ''' <param name="sonConDt"></param>
    ''' <param name="apsType"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function GetSonContainerStartEndDate(ByVal sonConDt As DataTable, ByVal apsType As String) As DataTable
        Dim strSql = <String>select ad.containerid,ai.apstype,TO_CHAR(ad.starttime,'YYYY-MM-DD hh24:mi:ss') starttime,TO_CHAR(ad.endtime,'YYYY-MM-DD hh24:mi:ss') endtime from containersubcontainer cc 
left join ApsPlanDetailpre ad on ad.containerid=cc.containerid and ad.workflowstepname=cc.workflowstepname and ad.isuse=0 
left join ApsPlanInfopre ai on ai.apsplaninfoid=ad.apsplaninfoid
where cc.subcontainerid='{0}'</String>.Value  '子卡对应父工序的排产计划信息
        For Each sonRow In sonConDt.Rows
            Dim faDate = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, sonRow("containerid").ToString))
            If faDate.Rows.Count > 0 Then
                Dim temp = faDate.Select("apsType='" + apsType + "'")
                If temp.Length > 0 Then
                    sonRow("STARTTIME") = temp(0)("STARTTIME")
                    sonRow("ENDTIME") = temp(0)("ENDTIME")
                Else
                    sonRow("STARTTIME") = faDate.Rows(0)("STARTTIME")
                    sonRow("ENDTIME") = faDate.Rows(0)("ENDTIME")
                End If
            Else
                sonRow("STARTTIME") = DBNull.Value
                sonRow("ENDTIME") = DBNull.Value
            End If
        Next
        sonConDt.AcceptChanges()
        Return sonConDt
    End Function

    ''' <summary>
    ''' 删除排产数据
    ''' </summary>
    ''' <param name="conApsID">排产ID</param>
    ''' <param name="seq">工序顺序</param>
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal conApsID As String, ByVal seq As Integer, Optional ByVal status As String = "")
        Dim strSql = <string> select al.apsplandetailid from ApsPlanDetailpre al 
left join workflowstep ws on ws.workflowstepid=al.workflowstepid
where al.apsplaninfoid='{0}' and al.isuse=0 and nvl(al.status,0)!=1 and ws.sequence>={1} </string>.Value
        Dim sqlList As New ArrayList

        Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(String.Format(strSql, conApsID, seq))

        For Each row In apsDetailDt.Rows
            sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete ApsPlanDetailpre al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
        Next

        DatabaseHelper.ExecuteSqlTran(sqlList)

        If DatabaseHelper.GetSingle("select 1 from ApsPlanDetailpre al where al.apsplaninfoid='" + conApsID + "'") Is Nothing Then
            DatabaseHelper.ExecuteSql("delete ApsPlanInfopre where ApsPlanInfoid='" + conApsID + "'")
        End If

    End Sub

    ''' <summary>
    ''' 删除排产数据
    ''' </summary> 
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal containerId As String, ByVal stepIds As String, Optional ByVal type As String = "1")
        Dim strSql = <string> select ai.apsplaninfoid,al.apsplandetailid from apsplaninfopre ai
left join ApsPlanDetailpre al on al.apsplaninfoid=ai.apsplaninfoid 
where ai.containerId='{0}' and ai.isuse=0 and al.isuse=0 and ai.apstype='{1}'</string>.Value

        strSql = String.Format(strSql, containerId, type)
        strSql += String.Format(" and al.workflowstepid in ({0}) ", stepIds)
        strSql += " and nvl(al.status,0)!=1 "

        Dim sqlList As New ArrayList

        Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(strSql)

        If apsDetailDt.Rows.Count = 0 Then
            Return
        End If

        For Each row In apsDetailDt.Rows
            sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            sqlList.Add(String.Format("delete ApsPlanDetailpre al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
        Next

        DatabaseHelper.ExecuteSqlTran(sqlList)

        If DatabaseHelper.GetSingle("select 1 from ApsPlanDetailpre al where al.apsplaninfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'") Is Nothing Then
            DatabaseHelper.ExecuteSql("delete ApsPlanInfopre where ApsPlanInfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'")
        End If


    End Sub

    ''' <summary>
    ''' 删除排产数据
    ''' </summary> 
    ''' <remarks></remarks>
    Public Overrides Sub DeleteApsPlanData(ByVal apsplandetailIds As List(Of String))

        For Each id As String In apsplandetailIds
            Dim strSql = <string> select ai.apsplaninfoid,al.apsplandetailid from apsplaninfopre ai
left join ApsPlanDetailpre al on al.apsplaninfoid=ai.apsplaninfoid 
where  ai.isuse=0 and al.isuse=0 </string>.Value
             
            strSql += String.Format(" and al.apsplandetailid='{0}' ", id)
            strSql += " and nvl(al.status,'0')='0' "

            Dim sqlList As New ArrayList

            Dim apsDetailDt = DatabaseHelper.Query_ReturnDatTable(strSql)

            If apsDetailDt.Rows.Count = 0 Then
                Return
            End If

            For Each row In apsDetailDt.Rows
                sqlList.Add(String.Format("delete apsplandetailemptime ae2 where ae2.apsplandetailempid in (select ae.apsplandetailempid from apsplandetailemp ae where ae.apsplandetailid='{0}')", row("apsplandetailid").ToString))
                sqlList.Add(String.Format("delete apsplandetailemp ae where ae.apsplandetailid='{0}'", row("apsplandetailid").ToString))
                sqlList.Add(String.Format("delete ApsPlanDetailpre al where al.apsplandetailid='{0}'", row("apsplandetailid").ToString))
            Next

            DatabaseHelper.ExecuteSqlTran(sqlList)

            If DatabaseHelper.GetSingle("select 1 from ApsPlanDetailpre al where al.apsplaninfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'") Is Nothing Then
                DatabaseHelper.ExecuteSql("delete ApsPlanInfopre where ApsPlanInfoid='" + apsDetailDt.Rows(0)("apsplaninfoid").ToString + "'")
            End If

        Next

       

    End Sub

    '----------------------------------------------

    ''' <summary>
    ''' 有限产能排产，数据查询
    ''' add:Wangjh 20220424
    ''' </summary>
    ''' <param name="para"></param>
    ''' <param name="isPage">是否分页</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function GetApsPoolContainerInfo(ByVal para As Dictionary(Of String, String), Optional ByVal isPage As Boolean = False) As uMESProcessResult
        Dim strsql As String = GetApsPoolContainerSql(para)
        Dim result As New uMESProcessResult

        If isPage Then '分页
            Dim paraPage As New uMESPagingDataDTO
            paraPage.CurrentPageIndex = Convert.ToInt32(para("CurrentPageIndex"))
            paraPage.PageSize = Convert.ToInt32(para("PageSize").ToString)
            paraPage.strSQL = strsql
            result = DatabaseHelper.RunProcedureForPage(paraPage)
        Else '不分页
            result.Result = True
            result.DBTable = DatabaseHelper.Query_ReturnDatTable(strsql)
        End If


        '批次的实际开始时间REALSTARTDATE：到料时间>计划开始时间=到料时间,到料时间为空：预计到料或齐套时间>计划开始时间=预计到料或齐套时间
        '如果已经在制了，开始时间则是当前时间
        For Each row As DataRow In result.DBTable.Rows
            If String.IsNullOrWhiteSpace(row("MATERIALDATE").ToString) Then
                If Not String.IsNullOrWhiteSpace(row("ExpectMaterialDate").ToString) Then '预计到料或齐套时间
                   row("REALSTARTDATE") = row("ExpectMaterialDate")
                End If
            Else
                If Date.Parse(row("MATERIALDATE").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                    row("REALSTARTDATE") = row("MATERIALDATE")
                End If
            End If

        Next

        result.DBTable.AcceptChanges()

        Return result
    End Function

    ''' <summary>
    ''' 有限排产查询sql语句
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetApsPoolContainerSql(ByVal para As Dictionary(Of String, String)) As String
        Dim strSql = <string>SELECT distinct CCI.MFGORDERNAME,CCI.CONTAINERID,CCI.CONTAINERNAME,CCI.PRODUCTDESCRIPTION PRODUCTDESC,CCI.PRODUCTREVISION,CCI.QTY,CCI.WORKFLOWSTEPNAME,
                CCI.CONTAINERTYPE,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,
                W.WORKFLOWID,WB.WORKFLOWNAME,W.WORKFLOWREVISION,WB.WORKFLOWNAME||':'||W.WORKFLOWREVISION WORKFLOWINFO,wc.apspoolid,
                TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,
                to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,
                to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate ,TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') REALSTARTDATE, 
                nvl2(cc.containersubcontainerid,'1','') ISSUBCON,cci.togetherflag,pc.prioritycodename,cci.CriticalValue,cci.StepDuration,cci.ApsSequence
                ,TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') STARTTIME,2 DJDKSPECIAL,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') ENDTIME,
                decode(cci.ISINSTORE,-1,0,cci.workflowstepsequence-1) beginsequence,f.factoryname,wct.workcentername,te.teamname
                FROM CONTAINERCURRENTINFO CCI
                left join apspoolworkcenter wc on cci.fromworkcenterid=wc.workcenterid
                left join apspooljhyemp ae on ae.apspoolid=wc.apspoolid 
                left join apspool ap on ae.apspoolid=ap.apspoolid and ap.isuse=0     
                LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
                left join prioritycode pc on pc.prioritycodeid=m.priorityid
                LEFT JOIN ORDERSTATUS o ON o.orderstatusid = M.orderstatusid
                LEFT JOIN CONTAINER C ON C.CONTAINERID=CCI.CONTAINERID
                left join WORKFLOW W ON W.WORKFLOWID=DECODE(CCI.ISINSTORE,-1,C.MFGWORKFLOWID,CCI.WORKFLOWID)
                LEFT JOIN WORKFLOWBASE WB ON WB.WORKFLOWBASEID=W.WORKFLOWBASEID
                left join containersubcontainer cc on cc.subcontainerid=cci.containerid and rownum=1

                LEFT JOIN productteaminfo pti ON pti.productname = cci.productname AND pti.isuse = 0
                LEFT JOIN team te ON te.teamid = pti.teamid
                LEFT JOIN workcenter wct ON wct.workcenterid = te.workcenterid
                LEFT JOIN factory f ON f.factoryid = wct.factoryid

                WHERE CCI.CONTAINERSTATUS=1 and cci.isinpool=1 and cci.isinstore != 1</string>.Value
        'yuxj20221024   取消AND CCI.DISPATCHNUM=0
        strSql += " and (o.orderstatusid IS NULL OR o.ORDERSTATUSNAME <> '关闭') " '订单状态

        If para.ContainsKey("ContainerName") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerName")) Then
                strSql += String.Format(" and cci.ContainerName like '%{0}%' ", para("ContainerName"))
            End If
        End If

        If para.ContainsKey("ProductName") Then
            If Not String.IsNullOrWhiteSpace(para("ProductName")) Then
                strSql += String.Format(" and cci.ProductName like '%{0}%' ", para("ProductName"))
            End If
        End If

        If para.ContainsKey("MfgorderName") Then
            If Not String.IsNullOrWhiteSpace(para("MfgorderName")) Then
                strSql += String.Format(" and cci.MfgorderName like '%{0}%' ", para("MfgorderName"))
            End If
        End If

        If para.ContainsKey("FactoryID") Then
            If Not String.IsNullOrWhiteSpace(para("FactoryID")) Then
                strSql += String.Format(" and cci.originalfactoryid ='{0}' ", para("FactoryID"))
            End If
        End If

        If para.ContainsKey("PoolID") Then '排产池
            If Not String.IsNullOrWhiteSpace(para("PoolID")) Then
                strSql += String.Format(" and ap.apspoolid ='{0}' ", para("PoolID"))
            End If

            If para.ContainsKey("EmployeeID") Then '根据计划员筛选排产池
                If Not String.IsNullOrWhiteSpace(para("EmployeeID")) Then
                    strSql += String.Format(" and ae.EmployeeID like '%{0}%' ", para("EmployeeID"))
                End If
            End If
        End If

        '开始时间设定范围
        If para.ContainsKey("OrderBStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBStartDate"))
            End If
        End If

        If para.ContainsKey("OrderEStartDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEStartDate")) Then
                strSql += String.Format(" and M.plannedstartdate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEStartDate"))
            End If
        End If
        '结束时间设定范围
        If para.ContainsKey("OrderBEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderBEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate >= to_date('{0}','YYYY-MM-DD') ", para("OrderBEndDate"))
            End If
        End If

        If para.ContainsKey("OrderEEndDate") Then
            If Not String.IsNullOrWhiteSpace(para("OrderEEndDate")) Then
                strSql += String.Format(" and M.plannedcompletiondate <= to_date('{0}','YYYY-MM-DD') ", para("OrderEEndDate"))
            End If
        End If

        '手动指定日期
        If para.ContainsKey("RealBStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealBStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate >= to_date('{0}','YYYY-MM-DD') ", para("RealBStartDate2"))
            End If
        End If

        If para.ContainsKey("RealEStartDate2") Then
            If Not String.IsNullOrWhiteSpace(para("RealEStartDate2")) Then
                strSql += String.Format(" and cci.ExpectMaterialDate <= to_date('{0}','YYYY-MM-DD') ", para("RealEStartDate2"))
            End If
        End If

        If para.ContainsKey("WorkflowStepName") Then
            If Not String.IsNullOrWhiteSpace(para("WorkflowStepName")) Then
                strSql += String.Format(" and cci.WORKFLOWSTEPNAME like '%{0}%' ", para("WorkflowStepName"))
            End If
        End If

        If para.ContainsKey("workcenterid") Then
            If Not String.IsNullOrWhiteSpace(para("workcenterid")) Then
                strSql += String.Format(" AND wct.workcenterid = '{0}'", para("workcenterid"))
            End If
        End If

        If para.ContainsKey("teamid") Then
            If Not String.IsNullOrWhiteSpace(para("teamid")) Then
                strSql += String.Format(" AND te.teamid = '{0}'", para("teamid"))
            End If
        End If

        '增加是否已排产过滤 add by zhangrj 20250114
        If para.ContainsKey("IsTryAps") Then
            If para("IsTryAps") = "1" Then
                strSql += " AND NOT EXISTS( SELECT 1 FROM apsplaninfopre ai WHERE ai.containerid = cci.containerid) "
            End If
        End If

        '排序
        strSql += " order by ApsSequence asc, pc.prioritycodename "
        Return strSql
    End Function

    ''' <summary>
    ''' 获取批次的排产信息
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetContainerLMAps(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select cci.mfgordername,cci.containername,cci.productdescription productdesc,cci.qty,wb.workflowname||':'||w.workflowrevision workflowinfo,ai.apstype,
TO_CHAR(ai.starttime,'YYYY-MM-DD HH24:MI:SS') starttime,TO_CHAR(ai.endtime,'YYYY-MM-DD HH24:MI:SS') endtime,w.workflowid,cci.containerid,cci.containertype,cci.productid,ai.apsplaninfoid,
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') MFGSTARTDATE,TO_CHAR(M.PLANNEDCOMPLETIONDATE,'YYYY-MM-DD') MFGENDDDATE,to_char(cci.ExpectMaterialDate,'yyyy-mm-dd hh24:mi:ss') ExpectMaterialDate,to_char(cci.materialdate,'yyyy-mm-dd hh24:mi:ss') materialdate, 
TO_CHAR(M.PLANNEDSTARTDATE,'YYYY-MM-DD') REALSTARTDATE,cci.workflowstepname,DECODE(CCI.CONTAINERTYPE,2,'批量加工',3,'批量带子序号', 7,'单件加工') AS CONTAINERTYPENAME,
cci.productname,cci.productrevision,wb.workflowname,w.workflowrevision,cci.togetherflag  from ApsPlanInfoPre ai
left join containercurrentinfo cci on cci.containerid=ai.containerid
LEFT JOIN MFGORDER M ON M.MFGORDERID=CCI.MFGORDERID
left join workflow w on w.workflowid=ai.workflowid
left join workflowbase wb on wb.workflowbaseid=w.workflowbaseid
where ai.apstype2=2  and ai.isuse=0 </string>.Value '

        If para.ContainsKey("ApsType") Then
            If Not String.IsNullOrWhiteSpace(para("ApsType")) Then
                strSql += String.Format(" and ai.apstype='{0}' ", para("ApsType"))
            End If
        End If

        Dim condition As String = ""
        For Each r In conDt.Rows
            condition += " (1=1 "
            If conDt.Columns.Contains("containerid") And conDt.Columns.Contains("workflowid") Then
                condition += " and ai.containerid='" + r("containerid").ToString() + "'"
                condition += " and ai.workflowid='" + r("workflowid").ToString() + "'"
            End If
            If conDt.Columns.Contains("ApsType") Then
                condition += " and ai.ApsType=" + r("ApsType").ToString()
            End If
            condition += ") or"
        Next
        If condition <> "" Then
            condition = condition.Trim("r", "o")
            strSql += " and ( " + condition + " ) "
        End If

        Dim dt = DatabaseHelper.Query_ReturnDatTable(strSql)

        '批次的实际开始时间REALSTARTDATE：到料时间>计划开始时间=到料时间,到料时间为空：预计到料或齐套时间>计划开始时间=预计到料或齐套时间
        '如果已经在制了，开始时间则是当前时间
        For Each row As DataRow In dt.Rows
            If String.IsNullOrWhiteSpace(row("MATERIALDATE").ToString) Then
                If Not String.IsNullOrWhiteSpace(row("ExpectMaterialDate").ToString) Then '预计到料或齐套时间
                   row("REALSTARTDATE") = row("ExpectMaterialDate")
                End If
            Else
                If Date.Parse(row("MATERIALDATE").ToString).Date > Date.Parse(row("MFGSTARTDATE").ToString).Date Then
                    row("REALSTARTDATE") = row("MATERIALDATE")
                End If
            End If
        Next
        dt.AcceptChanges()
        Return dt
    End Function
    
    ''' <summary>
    ''' 获取工序级的排产信息,甘特图
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetStepLMAps(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string> select ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.containername,ai.starttime constarttime,ai.endtime conendtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  ad.starttime stepstarttime,ad.endtime stependtime from ApsPlanInfopre ai 
  left join containercurrentinfo cci on cci.containerid=ai.containerid
  left join ApsPlanDetailpre ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
  left join workflowstep wfs on wfs.workflowstepid=ad.workflowstepid
  where 1=1 and ai.isuse=0 and ai.ApsType2=2 </string>.Value
        strSql = strSql + " and wfs.sequence>=cci.workflowstepsequence "

        If para.ContainsKey("ApsPlanDetailID") Then
            strSql += String.Format(" and ad.ApsPlanDetailID='{0}' ", para("ApsPlanDetailID"))
        End If
        If para.ContainsKey("ApsPlanInfoID") Then
            strSql += String.Format(" and ad.ApsPlanInfoID='{0}' ", para("ApsPlanInfoID"))
        End If

        If para.ContainsKey("ApsPlanInfoIDs") Then
            strSql += String.Format(" and ad.ApsPlanInfoID in ({0}) ", para("ApsPlanInfoIDs"))
        End If

        If para.ContainsKey("ContainerID") Then
            strSql += String.Format(" and ad.ContainerID='{0}' ", para("ContainerID"))
        End If

        If para.ContainsKey("WorkflowStepID") Then
            strSql += String.Format(" and ad.WorkflowStepID='{0}' ", para("WorkflowStepID"))
        End If

        strSql += " order by ai.containerid,ai.workflowid,ad.workflowstepid "

        Return DatabaseHelper.Query_ReturnDatTable(strSql)

    End Function

    ''' <summary>
    ''' 导出数据查询
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function GetContainerLMApsExport(ByVal conDt As DataTable, ByVal apsplandetailids As String) As DataTable
        Dim strSql = <string>select al.apsplandetailid,decode(ai.apstype,1,'正排',2,'倒排',3,'瓶颈排') apstypename,cci.productname,cci.realcontainerno,wb.workflowname,w.workflowrevision,al.workflowstepname,wc.workcentername,t.teamname,
rd.description resourcedesc,rd.resourcename,to_char(al.starttime,'yyyy/mm/dd hh24:mi:ss') starttime,to_char(al.endtime,'yyyy/mm/dd hh24:mi:ss') endtime,k.apsempname
from apsplaninfopre ai
left join apsplandetailpre al on al.apsplaninfoid=ai.apsplaninfoid and al.isuse=0
left join containercurrentinfo cci on cci.containerid=ai.containerid
left join workflow w on w.workflowid=ai.workflowid
left join workflowbase wb on wb.workflowbaseid=w.workflowbaseid
left join workcenter wc on wc.workcenterid=al.workcenterid
left join team t on t.teamid=al.teamid
left join resourcedef rd on rd.resourceid=al.resourceid
left join containerstepworkreport csw on csw.containerid=al.containerid and csw.workflowstepid=al.workflowstepid and csw.isuse=0
left join (
SELECT wm_concat(e.fullname) apsempname,em.apsplandetailid FROM apsplandetailemp em 
LEFT JOIN employee e ON e.employeeid = em.employeeid
WHERE nvl(em.isuse,0) = 0
GROUP BY em.apsplandetailid
)k ON k.apsplandetailid = al.apsplandetailid
where  ai.apstype2=2 and ai.isuse=0 and csw.containerstepworkreportid is null </string>.Value
        Dim condition = ""
        For Each con As DataRow In conDt.Rows
            condition += String.Format(" ( ai.containerid='{0}' and ai.workflowid='{1}' and ai.apstype='{2}' ) or", con("containerid").ToString, con("workflowid").ToString, con("apstype").ToString)
        Next
        If condition <> "" Then
            condition = condition.Trim("r", "o")
        End If
        If String.IsNullOrWhiteSpace(apsplandetailids) Then
            strSql += " and (" + condition + ")"
        End If
        If Not String.IsNullOrWhiteSpace(apsplandetailids) Then
            strSql += " and al.apsplandetailid IN ('" + apsplandetailids.Replace(",", "','") + "')"
        End If
        strSql += " order by cci.containername,al.workflowstepid "
        Return DatabaseHelper.Query_ReturnDatTable(strSql)
    End Function

#Region "有限排产，grid查看及调整"

    ''' <summary>
    ''' 获取工序级的排产信息
    ''' </summary>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function GetStepLMAps2(ByVal para As Dictionary(Of String, String)) As DataTable
        Dim strSql = <string>select  ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.mfgordername,cci.containername,to_char(ai.starttime,'yyyy-mm-dd hh24:mi:ss') constarttime,to_char(ai.endtime,'yyyy-mm-dd hh24:mi:ss') conendtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  to_char(ad.starttime,'yyyy-mm-dd hh24:mi:ss') stepstarttime,to_char(ad.endtime,'yyyy-mm-dd hh24:mi:ss') stependtime,nvl(sb.revofrcdid,ws.specid) specid,
  decode(re.resourcename,null,'',re.description||'('||re.resourcename||')') RESNAME,wc.workcentername,t.teamname,re.resourceid,wc.workcenterid,t.teamid,
  WS.MAKEEMPCOUNTS,WS.PREPARECYCLE,WS.MAKECYCLE,WS.TRANSFERCYCLE,WS.SHIPCYCLE,WS.COMMICYCLE,wm_concat(e.employeeid) employeeids,ws.sequence,to_char(ad.ResStartTime,'yyyy-mm-dd hh24:mi:ss') ResStartTime,
to_char(ad.ResEndTime,'yyyy-mm-dd hh24:mi:ss') ResEndTime,ad.qty from ApsPlanInfopre ai 
  left join containercurrentinfo cci on cci.containerid=ai.containerid
  left join ApsPlanDetailpre ad on ad.apsplaninfoid=ai.apsplaninfoid and ad.isuse=0
left join workflowstep ws on ws.workflowstepid=ad.workflowstepid
left join specbase sb on sb.specbaseid=ws.specbaseid
left join resourcedef re on re.resourceid=ad.resourceid
left join workcenter wc on wc.workcenterid=ad.workcenterid
left join team t on t.teamid=ad.teamid
left join ApsPlanDetailEmp ae on ae.apsplandetailid=ad.apsplandetailid
left join employee e on e.employeeid=ae.employeeid
  where 1=1 and ai.isuse=0 and ai.ApsType2=2 </string>.Value
        If para.ContainsKey("ApsPlanDetailID") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanDetailID")) Then
                strSql += String.Format(" and ad.ApsPlanDetailID='{0}' ", para("ApsPlanDetailID"))
            End If
        End If
        If para.ContainsKey("ApsPlanInfoID") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanInfoID")) Then
                strSql += String.Format(" and ad.ApsPlanInfoID='{0}' ", para("ApsPlanInfoID"))
            End If
        End If

        If para.ContainsKey("ApsPlanInfoIDs") Then
            If Not String.IsNullOrWhiteSpace(para("ApsPlanInfoIDs")) Then
                strSql += String.Format(" and ad.ApsPlanInfoID in ({0}) ", para("ApsPlanInfoIDs"))
            End If
        End If

        If para.ContainsKey("ContainerID") Then
            If Not String.IsNullOrWhiteSpace(para("ContainerID")) Then
                strSql += String.Format(" and ad.ContainerID='{0}' ", para("ContainerID"))
            End If
        End If

        If para.ContainsKey("WorkflowStepID") Then
            If Not String.IsNullOrWhiteSpace(para("WorkflowStepID")) Then
                strSql += String.Format(" and ad.WorkflowStepID='{0}' ", para("WorkflowStepID"))
            End If
        End If

        If para.ContainsKey("beginSequence") Then
            If Not String.IsNullOrWhiteSpace(para("beginSequence")) Then
                strSql += String.Format(" and ws.Sequence>={0} ", para("beginSequence"))
            End If
        End If

        If para.ContainsKey("EndSequence") Then
            If Not String.IsNullOrWhiteSpace(para("EndSequence")) Then
                strSql += String.Format(" and ws.Sequence<={0} ", para("EndSequence"))
            End If
        End If

        If para.ContainsKey("ResourceID") Then
            If Not String.IsNullOrWhiteSpace(para("ResourceID")) Then
                strSql += String.Format(" and re.resourceid='{0}' ", para("ResourceID"))
            End If
        End If

        If para.ContainsKey("ContainDate") Then '包含的日期  
            If Not String.IsNullOrWhiteSpace(para("ContainDate")) Then '举例：包含2022/6/26: 开始日期小于<2022/6/26 23:59:59 且结束日期大于2022/6/26 0:0:0 就算
                strSql += String.Format(" and ad.starttime-1<to_date('{0}','yyyy/mm/dd hh24:mi:ss') and ad.endtime>=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("ContainDate"))
            End If
        End If

        If para.ContainsKey("StartDate") Then
            If Not String.IsNullOrWhiteSpace(para("StartDate")) Then
                strSql += String.Format(" and ad.starttime>=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("StartDate"))
            End If
        End If

        If para.ContainsKey("EndDate") Then
            If Not String.IsNullOrWhiteSpace(para("EndDate")) Then
                strSql += String.Format(" and ad.endtime<=to_date('{0}','yyyy/mm/dd hh24:mi:ss') ", para("EndDate"))
            End If
        End If

        If para.ContainsKey("Status") Then
            If Not String.IsNullOrWhiteSpace(para("Status")) Then
                strSql += String.Format(" and ad.status={0} ", para("Status"))
            End If
        End If

        strSql += <string> group by ai.ApsType,ai.containerid,ai.apsplaninfoid,cci.mfgordername,cci.containername,ai.starttime,ad.endtime,ai.workflowid,ad.apsplandetailid,ad.workflowstepid,ad.workflowstepname,
  ad.starttime,ai.endtime,sb.revofrcdid,ws.specid,re.resourcename,re.description,wc.workcentername,t.teamname,re.resourceid,wc.workcenterid,t.teamid,ad.apsplandetailid,
  WS.MAKEEMPCOUNTS,WS.PREPARECYCLE,WS.MAKECYCLE,WS.TRANSFERCYCLE,WS.SHIPCYCLE,WS.COMMICYCLE,ws.sequence,ad.ResStartTime,ad.ResEndTime,ad.qty </string>.Value

        strSql += " order by ai.containerid,ai.workflowid,ad.workflowstepid,ai.ApsType "

        Return DatabaseHelper.Query_ReturnDatTable(strSql)

    End Function

#End Region

#Region "有限产能，正排"
    ''' <summary>
    ''' 有限产能，正排
    ''' 日历：分厂日历>设备日历>人员日历 --颗粒度变细
    ''' 班次：设备班次>人员班次 --颗粒度变细
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <remarks></remarks>
    Public Overrides Function LMPositiveCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False,
                                                  Optional ByVal ApsOccupyConIdList As List(Of String) = Nothing, Optional ByVal apsTempTable As String = "") As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsType = "1"
        If IsBottleneck Then
            apsType = "3"
        End If

        Dim alreadyApsConIdList As New List(Of String)  '本排产池本次已经排产的批次id列表
        For Each r As DataRow In conDt.Rows '循环批次

            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            Dim apsPoolDt As New DataTable '排产池信息
            If conDt.Columns.Contains("apspoolid") Then
                apsPoolDt = Aps_GetApsPoolDt(r("apspoolid").ToString)
            Else
                apsPoolDt = Aps_GetApsPoolDt(conBaseRow("apspoolid").ToString)
            End If
            '排产截止日期
            Dim deadlineDate As Date = New Date(9999, 1, 1)
            If apsPoolDt.Rows.Count > 0 Then
                If Not apsPoolDt.Rows(0).IsNull("TryCycle") Then
                    deadlineDate = Date.Now.Date.AddDays(Val(apsPoolDt.Rows(0)("TryCycle")) + 1)
                End If
            End If


            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim startTime = Date.Parse(r("STARTTIME"))
            'startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("BEGINSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, Val(r("BEGINSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If

            '已排产信息
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            Dim orgStepDt = stepDt.Copy
            stepDt = FilterApsStep(orgStepDt, stepApsDt, r)

            '二次筛选后，不参与的序，清除未下发的排产记录
            Dim clearStepId As New List(Of String)
            For Each orgStepRow As DataRow In orgStepDt.Rows
                If stepDt.Select("workflowstepid='" + orgStepRow("workflowstepid").ToString + "'").Length = 0 Then
                    clearStepId.Add(orgStepRow("workflowstepid").ToString)
                End If
            Next
            If clearStepId.Count > 0 Then
                DeleteApsPlanData(r("containerid").ToString, "'" + String.Join(",", clearStepId).Replace(",", "','") + "'", "1")
            End If

            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    Return result
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "批次" + r("containername").ToString + "未找到可排产的工序"
                    'Return result
                    Continue For '执行后面的批次yuxj20230307
                End If
            End If

            Dim curTime = startTime
            '真正排产开始时间，以排产池规则运算后得出,若是手动调整后的重新运算，暂不进行此逻辑
            If Not r.Table.Columns.Contains("manualAdjust") Then
                curTime = GetConApsStartTime(curTime, r, conBaseRow, apsPoolDt)
            End If

            Dim stepPlanSqls As New ArrayList

            For Each stepRow As DataRow In stepDt.Rows

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                Dim _startTime = curTime '此工序的初期开始时间               

                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                'For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                '    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                '        apsOccupyResDT.Rows.RemoveAt(i)
                '    End If
                'Next
                'apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)
                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)
                '设备模拟加工完成

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString '是否多设备并行加工
                If isMachineParallel = "1" Then '并行设备加工下，任何一台设备超过截止日期，此批次终止后续排产
                    If resSimulationDt.AsEnumerable().Any(Function(resSLRow) Date.Parse(resSLRow("simulateEnd").ToString()).Date >= deadlineDate) Then
                        '到截止日期后，若后续的工序还有以前的排产数据，则应该清除
                        DeleteApsPlanData(conApsID, Integer.Parse(stepRow("sequence").ToString))
                        Exit For
                    End If
                Else '非行设备加工下，去除超过截止日期的记录，若最终剩余0个设备，此批次终止后续排产
                    Dim resSimulationDt2 = resSimulationDt.AsEnumerable().Where(Function(resSLRow) Date.Parse(resSLRow("simulateEnd").ToString()).Date < deadlineDate)
                    If resSimulationDt2.Count = 0 Then
                        '到截止日期后，若后续的工序还有以前的排产数据，则应该清除
                        DeleteApsPlanData(conApsID, Integer.Parse(stepRow("sequence").ToString))
                        Exit For
                    End If
                    resSimulationDt = resSimulationDt2.CopyToDataTable
                End If

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员

                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, makeEmpCounts, isMachineParallel)

                '到此此工序模拟加工完成，取出计算后的，工序的加工信息               
                'Dim confirmResSL As DataRow = recResSimulationDt.Rows(0)

                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)

                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString

                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1 Step 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql

                    Dim stepApsId As String = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(i)("Apsplandetailid")
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                 confirmResSL("resourceid"), stepRow("specfactoryid").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty").ToString, conApsID, stepRow("workflowstepid").ToString, stepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                 confirmResSL("resourceid"), para("EmployeeID"), stepRow("specfactoryid").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty").ToString))
                    End If

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next

            Next

            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            If conApsDt2.Rows.Count > 0 Then
                '生成批次计划sql
                stepPlanSqls.Clear()
                If conApsDt.Rows.Count > 0 Then
                    stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
                Else
                    stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, apsType, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                                   r("workflowid").ToString))
                End If
                '执行存入批次
                DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            End If

            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If

        Next
        result.Result = True
        Return result
    End Function

    ''' <summary>
    ''' 为设备列表增加qty信息
    ''' </summary>
    ''' <param name="isMachineParallel">是否并行加工</param>
    ''' <param name="dtRec">设备列表</param>
    ''' <param name="qty">批次数量</param>
    ''' <remarks></remarks>
    Sub calcResourceAndQty(ByVal isMachineParallel As String, ByRef dtRec As DataTable, ByVal qty As String)
        If Not dtRec.Columns.Contains("qty") Then
            dtRec.Columns.Add("qty")
        End If

        If isMachineParallel = "0" Then
            Dim confirmResSL As DataRow = dtRec.Rows(0)
            dtRec = dtRec.Clone
            dtRec.ImportRow(confirmResSL)
            dtRec.AcceptChanges()
            dtRec.Rows(0)("qty") = qty
        ElseIf isMachineParallel = "1" Then
            '将数量拆分
            Dim containerQty As Integer = Integer.Parse(qty) '批次数量
            Dim imod As Integer = Integer.Parse(qty) Mod dtRec.Rows.Count
            Dim iavg As Integer = Integer.Parse(qty) / dtRec.Rows.Count
            For i As Integer = 0 To dtRec.Rows.Count - 1 Step 1
                If i = dtRec.Rows.Count - 1 Then
                    dtRec.Rows(i)("qty") = iavg + imod
                Else
                    dtRec.Rows(i)("qty") = iavg
                End If
            Next
        End If
    End Sub

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，EndDate:结束时间
    ''' </summary>
    ''' <param name="simulateStart"></param>
    ''' <param name="simulateEnd"></param>
    ''' <param name="stepResMKDt"></param>
    ''' <param name="resUpKeepDt"></param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle"></param>
    ''' <param name="stepCycle"></param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMAscSLResEndDate(ByVal resourceId As String, ByVal simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                          ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                          ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)

        simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(1, simulateEnd, stepResMKDt, 1)
                    simulateEnd = simulateEnd.AddDays(offset + 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resRunCycle2, _stepShiftDt, stepResMKDt, resourceId)
        Else '递归进来的情况
            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateEnd, resShiftStart) '设备开始时间

            '------------更新班次最早最晚时间 add by zhangrj 20250923----------------
            Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateEnd) '获取设备在这一天的新的班次明细
            For Each drd As DataRow In dtDayWorkshift.Rows
                Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
                Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
                If resStart > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                    resStart = rStart
                End If
                If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                    resEnd = rEnd
                End If
            Next
            '----------------------------

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resEnd = resEnd.AddDays(1)
            End If

            If resEnd < simulateEnd Then '例如：工序18：30结束，而设备默认17：30结束，追加一天
                Dim differ = GetDifferMins(simulateEnd, resEnd)

                simulateEnd = resEnd
                simulateEnd = simulateEnd.AddDays(1)
                simulateEnd = simulateEnd.AddDays(GetDateOffset(1, simulateEnd, stepResMKDt, 0))

                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt, resourceId)
            ElseIf simulateEnd < resStart Then '结束时间，在设备未开始，eg:工序7:30结束，而设备8：30开始----->实际结束：8：30+1h
                Dim differ = GetDifferMins(resStart, simulateEnd)
                simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, differ, _stepShiftDt, stepResMKDt, resourceId)
            Else '正常情况，结束时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt, dtDayWorkshift)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '{``````[````}`````] 情况下才会只让 结束时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        'Dim removeRowIndex As New List(Of Integer) '需要移除的行
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1
            Dim resUpKeepRow = resUpKeepDt.Rows(i)

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If upkeepStart >= simulateStart And upkeepStart <= simulateEnd Then
                If upkeepEnd <= simulateEnd Then '{`````[````]````}
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf upkeepEnd > simulateEnd Then '{``````[````}`````]
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)

                    resUpKeepDt.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resUpKeepOffset2 += GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If

        Next

        simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resUpKeepOffset2, _stepShiftDt, stepResMKDt, resourceId)
        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？

        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1
            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If apsStartTime >= simulateStart And apsStartTime <= simulateEnd Then
                If apsEndTime <= simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf apsEndTime > simulateEnd Then
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resApsOccupyOffset2 += GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If
        Next
        simulateEnd = GetResApsEndOrStartDate(1, simulateEnd, resApsOccupyOffset2, _stepShiftDt, stepResMKDt, resourceId)
        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            LMAscSLResEndDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, False)
        End If

    End Sub

    ''' <summary>
    ''' 设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Asc:正排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="simulateStart">设备模拟加工开始时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次信息详细</param>
    ''' <param name="resShiftStart">设备最早开始班次</param>
    ''' <param name="resShiftEnd">设备最晚结束班次</param>
    ''' <param name="resCycle">设备运转周期(分钟)</param>
    ''' <remarks></remarks>
    Sub LMAscSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String, ByVal resCycle As Single)

        '计算设备模拟加工开始时间

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0)) '检查工作日历,开始时间顺移

        '班次因素
        Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备初期开始时间
        Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备初期结束时间
        '------查询新增班次日历的数据，更新班次最早最晚时间 add by zhangrj 20250923------------
        Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateStart) '获取设备在这一天的新的班次明细
        For Each drd As DataRow In dtDayWorkshift.Rows
            Dim rStart = GetRunStartOrEndDate(simulateStart, drd("starttime")) '开始时间
            Dim rEnd = GetRunStartOrEndDate(simulateStart, drd("endtime")) '结束时间
            If resStart > rStart Then '如果设备初始开始时间大于该班次的开始时间，则更新设备初始开始时间
                resStart = rStart
            End If
            If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                resEnd = rEnd
            End If
        Next
        '----------------------------

        'Dim differ = GetDifferMins(simulateStart, resStart)

        If simulateStart <= resStart Then '工序开始7：30 ，设备默认开始8：30
            simulateStart = resStart
        ElseIf resStart < simulateStart And simulateStart < resEnd Then '工序开始时间在设备周期内,工序开始：9：30，而设备默认开始8：30,此时要考虑休息时间段如果9:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateStart = GetResApsStartOrEndDate(1, simulateStart, _stepShiftDt, dtDayWorkshift)
            End If

        Else '此情况例子，工序开始时间：18：30，而设备结束时间17：30，设备开始时间就要追加一天,跨天考虑日历情况
            simulateStart = resStart
            simulateStart = simulateStart.AddDays(1)

        End If
        '日历检查
        simulateStart = simulateStart.AddDays(GetDateOffset(1, simulateStart, stepResMKDt, 0))

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '[`````{```]````} 或者 '[`````{````}```]情况下才会让 开始时间 偏移


        Dim resUpKeepOffset1 = 0 '保养检查后，需要偏移的分钟数，开始时间
        Dim lastUpKeepEndTime = simulateStart '设备最新保养占用的最大时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟开始时间在保养周期内才会偏移
            If Not (lastUpKeepEndTime >= upkeepStart And lastUpKeepEndTime < upkeepEnd) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If upkeepEnd <= lastUpKeepEndTime Then
                Continue For
            End If

            resUpKeepOffset1 += GetDifferMins(upkeepEnd, lastUpKeepEndTime)
            lastUpKeepEndTime = upkeepEnd
        Next

        simulateStart = simulateStart.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 = 0 '排产占用后，需要偏移的分钟数 
        Dim lastOccupyEndTime = simulateStart '设备最新占用的最大时间
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("resstarttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("resendtime").ToString)

            '模拟开始时间在占用周期内才会偏移
            If Not (lastOccupyEndTime >= apsStartTime And lastOccupyEndTime < apsEndTime) Then
                Continue For
            End If
            '必须大于上次占用最大时间才偏移
            If apsEndTime <= lastOccupyEndTime Then
                Continue For
            End If

            resApsOccupyOffset1 += GetDifferMins(apsEndTime, lastOccupyEndTime)
            lastOccupyEndTime = apsEndTime
        Next

        simulateStart = simulateStart.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            LMAscSLResStartDate(resourceId, simulateStart, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        End If

    End Sub

    ''' <summary>
    ''' 正排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>     
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMAscSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date
                                   ) As DataTable


        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间
        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCyCle2 As Single = 0 '设备加工周期(去除了转运时间)
        For Each dr As DataRow In resourceTable.Rows
            Dim reourceId As String = dr("resourceid").ToString
            stepCycle = Single.Parse(dr("stepCycle").ToString)
            stepCyCle2 = Single.Parse(dr("stepCyCle2").ToString)

            Dim simulateStart = startTime '模拟开始时间
            Dim simulateEnd = endTime '模拟结束时间

            Dim simulateStart2 = startTime '设备模拟开始时间
            Dim simulateEnd2 = endTime '设备模拟结束时间

            Dim resultRow As DataRow = result.NewRow
            resultRow("resourceid") = reourceId

            '+设备本身日历，设备上有日历用设备的日历，无则用工厂日历
            Dim _stepResMKDt = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
            End If

            '取出此设备的班次加工信息
            Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
            Dim _stepShiftDt = stepShiftDt.Clone
            Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
            If _stepShiftDrs.Length > 0 Then
                _stepShiftDt = _stepShiftDrs.CopyToDataTable
                resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
            End If


            '----计算设备的工作周期(分钟)
            Dim resCycle = 1439
            If _stepShiftDt.Rows.Count > 1 Then
                resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
            End If

            '此设备的保养计划
            Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")
            Dim _stepResUpkeepDt As New DataTable
            If resUpKeepRows.Length > 0 Then
                _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
            End If

            '此设备占用数据
            Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
            Dim _apsOccupyResDT As New DataTable
            If resApsOccupyRows.Length > 0 Then
                _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
            End If

            '计算工序模拟加工的开始时间
            LMAscSLResStartDate(reourceId, simulateStart, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
            simulateEnd = simulateStart
            '计算工序模拟加工的结束时间
            LMAscSLResEndDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

            resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

            '计算设备模拟加工的开始时间
            'LMAscSLResStartDate(reourceId, simulateStart2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
            simulateStart2 = simulateStart
            simulateEnd2 = simulateStart2
            '计算设备模拟加工的结束时间
            LMAscSLResEndDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCyCle2, True)

            resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("qty") = dr("qty").ToString
            result.Rows.Add(resultRow)
        Next

        result.AcceptChanges()

        Return result

    End Function

    ''' <summary>
    ''' 设备模拟加工数据追加优先级，分配人员
    ''' 预排产不分配到人,此方法已删除分配到人的逻辑
    ''' </summary>
    ''' <param name="resSimulationDt">设备模拟加工数据</param>
    ''' <param name="stepReourceDt">工序设备</param>
    ''' <param name="stepEmp">工序人员</param>
    ''' <param name="makeEmpCounts">工序加工人数</param>
    ''' <param name="isMachineParallel">设备是否并行加工</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function LMAscSLResEmp(ByVal resSimulationDt As DataTable, ByVal stepReourceDt As DataTable, ByVal stepEmp As DataTable, ByVal makeEmpCounts As Integer, Optional ByVal isMachineParallel As String = "0") As DataTable
        '此时应取出模拟加工结束最近的设备，把优先级追加上
        Dim recentEndDate = Nothing
        If Not resSimulationDt.Columns.Contains("prioritynum") Then
            resSimulationDt.Columns.Add(New DataColumn("prioritynum", System.Type.GetType("System.Int32")))
        End If

        For Each resSl In resSimulationDt.Rows
            '追加优先级
            If stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'").Length > 0 Then
                resSl("prioritynum") = stepReourceDt.Select("resourceid='" + resSl("resourceid").ToString + "'")(0)("prioritynum")
            End If
            If recentEndDate = Nothing Then
                recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
            Else
                If Date.Parse(resSl("simulateEnd").ToString) < recentEndDate Then
                    recentEndDate = Date.Parse(resSl("simulateEnd").ToString)
                End If
            End If
        Next
        resSimulationDt.AcceptChanges()

        '筛选加工时间等于最近时间，并优先级最大的设备
        Dim recResSimulationDt As DataTable = resSimulationDt.Clone '最近时间加工的

        '设备并行加工时此处不过滤设备
        If isMachineParallel = "0" Then
            For Each resSl In resSimulationDt.Rows
                If Date.Parse(resSl("simulateEnd").ToString) = recentEndDate Then
                    recResSimulationDt.ImportRow(resSl)
                    Exit For
                End If
            Next
        Else
            recResSimulationDt = resSimulationDt.Copy
        End If

        Dim recResSimulationView = recResSimulationDt.DefaultView()
        recResSimulationView.Sort = "prioritynum desc"
        recResSimulationDt = recResSimulationView.ToTable()
        recResSimulationDt.AcceptChanges()

        '设备模拟完成，分配到人

        '排除设备模拟加工时间段内，所有人都被占用
        '人员信息，即工序能加工的人员信息
        recResSimulationDt.Columns.Add("employeeids") '满足加工的人员信息



        recResSimulationDt.AcceptChanges()

        Return recResSimulationDt
    End Function

    
#End Region

#Region "有限产能，倒排"
    ''' <summary>
    ''' 有限产能，倒排
    ''' 日历：分厂日历>设备日历>人员日历 --颗粒度变细
    ''' 班次：设备班次>人员班次 --颗粒度变细
    ''' yuxj20221026 增加记录设备开始和结束时间，设备加工时间 包含工序准备时间 和 加工时间，不含转运时间
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <param name="IsBottleneck">是否是瓶颈排中的倒排</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMReverseCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String), Optional ByVal IsBottleneck As Boolean = False) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsType = "2"
        If IsBottleneck Then
            apsType = "3"
        End If

        Dim alreadyApsConIdList As New List(Of String)  '本排产池本次已经排产的批次id列表
        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim stepDt As New DataTable
            If conDt.Columns.Contains("ENDSEQUENCE") Then
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString, 0, Val(r("ENDSEQUENCE").ToString))
            Else
                stepDt = Aps_GetStepBaseDt(r("workflowid").ToString)  '工序
            End If

            '如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, apsType)) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If IsBottleneck Then
                If stepDt.Rows.Count = 0 Then
                    result.Result = True
                    Return result
                End If
            Else
                If stepDt.Rows.Count = 0 Then
                    result.Message = "批次" + r("containername").ToString + "未找到可排产的工序"
                    'Return result
                    Continue For '执行后面的批次yuxj20230307
                End If
            End If

            Dim endTime = Date.Parse(r("ENDTIME"))
            'endTime.AddDays(GetDateOffset(2, endTime, faMakeCalendarDt, 0)) '检查工作日历

            If endTime = endTime.Date Then
                endTime = endTime.AddMinutes(1439) '初期结束时间为23:59:00
            End If

            Dim curTime = endTime

            Dim stepCycle As Single = 0 '工序加工周期，单位分钟
            Dim stepCyCle2 As Single = 0 'yuxj设备加工周期，单位分钟
            Dim stepPlanSqls As New ArrayList

            For j As Integer = stepDt.Rows.Count - 1 To 0 Step -1
                Dim stepRow = stepDt.Rows(j)

                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                stepCycle = 0 '工序加工周期，单位分钟

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If

                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "批次" + r("containername").ToString + "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)

                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtBeforeTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()
                
                '此工序设备当前时间前的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtBeforeTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                Dim _startTime = curTime '此工序的初期开始时间

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMDescSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, makeEmpCounts, isMachineParallel)
                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)
                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString
                        
                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)
                    'yuxj30230309 不能只取第一行，不过有设备没有人时如何处理？？ 本逻辑不插入人员
                    'If recResSimulationDt.Select("employeeids is not null").Length > 0 Then
                    '    confirmResSL = recResSimulationDt.Select("employeeids is not null")(0)
                    'End If

                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql
                    curTime = Date.Parse(confirmResSL("simulateStart"))

                    '存入最佳设备和人员到排产计划表
                    '生成工序计划sql

                    Dim stepApsId As String = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(i)("Apsplandetailid")
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), conApsID, stepRow("workflowstepid").ToString, stepApsId))

                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                 stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), para("EmployeeID"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty")))

                    End If


                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                Next

            Next
            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            If conApsDt2.Rows.Count > 0 Then

                '生成批次计划sql
                stepPlanSqls.Clear()
                If conApsDt.Rows.Count > 0 Then
                    stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
                Else
                    stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, apsType, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                                   r("workflowid").ToString))
                End If
                '执行存入批次
                DatabaseHelper.ExecuteSqlTran(stepPlanSqls)
            End If
            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If
        Next

        result.Result = True
        Return result

    End Function

    ''' <summary>
    ''' 倒排模拟加工,工序各设备模拟加工情况
    ''' </summary>
    ''' <param name="resourceTable">设备集合</param>
    ''' <param name="faMakeCalendarDt">车间日历数据</param>
    ''' <param name="stepResMKDt">设备日历数据</param>
    ''' <param name="stepShiftDt">设备班次数据</param>
    ''' <param name="stepResUpkeepDt">设备保养数据</param>
    ''' <param name="startTime">工序初期开始时间</param>
    ''' <param name="endTime">工序初期结束时间</param>  
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function LMDescSimulationByRes(ByVal resourceTable As DataTable, ByVal faMakeCalendarDt As DataTable, ByVal stepResMKDt As DataTable,
                                  ByVal stepShiftDt As DataTable, ByVal stepResUpkeepDt As DataTable, ByVal apsOccupyResDT As DataTable, ByVal startTime As Date, ByVal endTime As Date) As DataTable

        Dim result As New DataTable
        result.Columns.Add("resourceid")
        result.Columns.Add("simulateStart") '模拟开始时间
        result.Columns.Add("simulateEnd") '模拟结束时间

        result.Columns.Add("simulateStart2") '设备模拟开始时间
        result.Columns.Add("simulateEnd2") '设备模拟结束时间
        result.Columns.Add("qty") '设备分配的数量：多机并行时按数量分配，非多机并行时各设备数量相同

        '设备的放假日历,车间日历+设备本身日历,初始化为车间日历
        Dim stepCycle As Single = 0 '工序加工周期
        Dim stepCycle2 As Single = 0 '增加设备加工周期
        For Each dr As DataRow In resourceTable.Rows
            Dim reourceId As String = dr("resourceid").ToString
            stepCycle = Single.Parse(dr("stepCycle").ToString)
            stepCycle2 = Single.Parse(dr("stepCyCle2").ToString)

            Dim simulateStart = startTime '模拟开始时间
            Dim simulateEnd = endTime '模拟结束时间

            Dim simulateStart2 = startTime '设备模拟开始时间
            Dim simulateEnd2 = endTime '设备模拟结束时间

            Dim resultRow As DataRow = result.NewRow
            resultRow("resourceid") = reourceId

            '+设备本身日历
            Dim _stepResMKDt = faMakeCalendarDt.Copy
            If stepResMKDt.Select("resourceid='" + reourceId + "'").Length > 0 Then
                _stepResMKDt = stepResMKDt.Select("resourceid='" + reourceId + "'").CopyToDataTable
            End If

            Dim resShiftStart = "00:00:00", resShiftEnd = "23:59:00" '设备班次最早开始和最晚结束时间
            Dim _stepShiftDt = stepShiftDt.Clone
            Dim _stepShiftDrs = stepShiftDt.Select("resourceid='" + reourceId + "'")
            If _stepShiftDrs.Length > 0 Then
                _stepShiftDt = _stepShiftDrs.CopyToDataTable
                resShiftStart = _stepShiftDt.Rows(0)("startdate").ToString
                resShiftEnd = _stepShiftDt.Rows(0)("enddate").ToString
            End If

            '----计算设备的工作周期(分钟)
            Dim resCycle = 1439
            If _stepShiftDt.Rows.Count > 1 Then
                resCycle = Val(_stepShiftDt.Compute("sum(mins)", "").ToString())
            End If

            '此设备的保养计划
            Dim resUpKeepRows = stepResUpkeepDt.Select("resourceid='" + reourceId + "'")

            Dim _stepResUpkeepDt As New DataTable
            If resUpKeepRows.Length > 0 Then
                _stepResUpkeepDt = resUpKeepRows.CopyToDataTable
            End If

            '此设备占用数据
            Dim resApsOccupyRows = apsOccupyResDT.Select("resourceid='" + reourceId + "'")
            Dim _apsOccupyResDT As New DataTable
            If resApsOccupyRows.Length > 0 Then
                _apsOccupyResDT = resApsOccupyRows.CopyToDataTable
            End If


            '计算工序模拟加工结束时间
            LMDescSLResEndDate(reourceId, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)

            simulateStart = simulateEnd

            '计算工序模拟加工开始时间
            LMDescSLResStartDate(reourceId, simulateStart, simulateEnd, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle, True)

            resultRow("simulateStart") = simulateStart.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd") = simulateEnd.ToString("yyyy-MM-dd HH:mm:ss")

            '计算设备模拟加工的结束时间,往前算去除转运时间stepCycle-stepCycle2
            simulateEnd2 = simulateEnd
            simulateStart2 = simulateEnd
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle - stepCycle2, True)
            simulateEnd2 = simulateStart2

            '计算设备模拟加工的开始时间
            simulateStart2 = simulateEnd2
            LMDescSLResStartDate(reourceId, simulateStart2, simulateEnd2, _stepResMKDt, _stepResUpkeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle, stepCycle2, True)

            resultRow("simulateStart2") = simulateStart2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("simulateEnd2") = simulateEnd2.ToString("yyyy-MM-dd HH:mm:ss")
            resultRow("qty") = dr("qty").ToString

            result.Rows.Add(resultRow)
        Next

        result.AcceptChanges()

        Return result

    End Function

    ''' <summary>
    ''' 设备模拟加工，结束时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，EndDate:结束时间 
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateEnd">设备模拟加工结束时间</param>
    ''' <param name="stepResMKDt">设备日历（包含车间日历）</param>
    ''' <param name="resUpKeepDt">设备保养信息</param>
    ''' <param name="_apsOccupyResDT">设备占用信息</param>
    ''' <param name="_stepShiftDt">设备班次详细信息eg:8:30-12:00,13:00-15:00,17:30-19:30</param>
    ''' <param name="resShiftStart">设备班次最早时间eg:8:30</param>
    ''' <param name="resShiftEnd">设备班次最晚时间eg:19:30</param>
    ''' <param name="resCycle">设备实际运转周期(去除休息时间)</param>
    ''' <remarks></remarks>
    Sub LMDescSLResEndDate(ByVal resourceId As String, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable, ByVal resUpKeepDt As DataTable,
                            ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                            ByVal resCycle As Integer)

        '1.考虑, 日历和班次的加工情况

        '日历因素
        simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0)) '检查工作日历,结束时间向前顺移

        '班次因素
        Dim resEnd = GetRunStartOrEndDate(simulateEnd, resShiftEnd) '设备初期结束时间
        '------------更新班次最晚时间 add by zhangrj 20250923----------------
        Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateEnd) '获取设备在这一天的新的班次明细
        For Each drd As DataRow In dtDayWorkshift.Rows
            'Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
            Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
            If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                resEnd = rEnd
            End If
        Next
        '----------------------------

        Dim differ = GetDifferMins(resEnd, simulateEnd)

        If differ < 0 Then '工序结束19：30 ，设备默认结束18：30
            simulateEnd = resEnd
        ElseIf differ <= resCycle Then '工序结束时间在设备周期内,工序结束：17：30，而设备结束18：30,,此时要考虑休息时间段如果17:30是休息，则又需順移

            If _stepShiftDt.Rows.Count > 0 Then
                simulateEnd = GetResApsStartOrEndDate(1, simulateEnd, _stepShiftDt, dtDayWorkshift)
            End If

        Else '此情况例子，工序结束时间：6：30，而设备开始时间8：30.设备结束时间18：30，真正结束时间就要向前推一天，跨天考虑日历

            simulateEnd = resEnd
            simulateEnd = simulateEnd.AddDays(-1)

            'simulateEnd = simulateEnd.AddDays(GetDateOffset(2, simulateEnd, stepResMKDt, 0))
            ''减去剩余的分钟数
            'simulateEnd = simulateEnd.AddMinutes(resCycle - differ)

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````}````]或者 '[`````{````}```]情况下才会让 结束时间向前偏移

        Dim resUpKeepOffset1 As Single = 0 '保养检查后，需要偏移的分钟数，开始时间
        Dim lastUpkeepStartTime = simulateEnd '设备最新保养占用的最小时间
        For Each resUpKeepRow In resUpKeepDt.Rows

            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            '模拟结束时间在保养周期内才会偏移
            If Not (lastUpkeepStartTime >= upkeepStart And lastUpkeepStartTime < upkeepEnd) Then
                Continue For
            End If
            '必须小于上次占用最早时间才偏移
            If upkeepEnd >= lastUpkeepStartTime Then
                Continue For
            End If

            resUpKeepOffset1 -= GetDifferMins(lastUpkeepStartTime, upkeepStart)
            lastUpkeepStartTime = upkeepStart
        Next

        simulateEnd = simulateEnd.AddMinutes(resUpKeepOffset1)

        '工序设备被占用情况
        Dim resApsOccupyOffset1 As Single = 0 '排产占用后，需要偏移的分钟数 
        Dim lastOccupyStartTime = simulateEnd '设备最新占用的最小时间
        For Each resApsRow In _apsOccupyResDT.Rows
            Dim apsStartTime = Date.Parse(resApsRow("resstarttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("resendtime").ToString)

            '模拟结束时间在占用周期内才会偏移
            If Not (lastOccupyStartTime >= apsStartTime And lastOccupyStartTime < apsEndTime) Then
                Continue For
            End If
            '必须小于上次占用最早时间才偏移
            If apsEndTime >= lastOccupyStartTime Then
                Continue For
            End If

            resApsOccupyOffset1 -= GetDifferMins(lastOccupyStartTime, apsStartTime)
            lastOccupyStartTime = apsStartTime
        Next

        simulateEnd = simulateEnd.AddMinutes(resApsOccupyOffset1)

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset1 = 0 And resApsOccupyOffset1 = 0) Then
            LMDescSLResEndDate(resourceId, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd, resCycle)
        End If

    End Sub

    ''' <summary>
    ''' 倒排设备模拟加工，开始时间计算
    ''' 命名规则:LM:有限，Desc:倒排，SL:模拟运算，StartDate:开始时间
    ''' </summary>
    ''' <param name="resourceId"></param>
    ''' <param name="simulateStart">模拟开始时间</param>
    ''' <param name="simulateEnd">模拟结束时间</param>
    ''' <param name="stepResMKDt">设备日历</param>
    ''' <param name="resUpKeepDt">设备保养</param>
    ''' <param name="_apsOccupyResDT">设备占用</param>
    ''' <param name="_stepShiftDt">设备班次</param>
    ''' <param name="resShiftStart">设备班次最早开始时间</param>
    ''' <param name="resShiftEnd">设备班次最晚结束时间</param>
    ''' <param name="resCycle">设备运转周期（排除休息时间）</param>
    ''' <param name="stepCycle">工序(设备)需要加工周期</param>
    ''' <param name="isFirst"></param>
    ''' <remarks></remarks>
    Sub LMDescSLResStartDate(ByVal resourceId As String, ByRef simulateStart As Date, ByRef simulateEnd As Date, ByVal stepResMKDt As DataTable,
                             ByVal resUpKeepDt As DataTable, ByVal _apsOccupyResDT As DataTable, ByVal _stepShiftDt As DataTable, ByVal resShiftStart As String, ByVal resShiftEnd As String,
                             ByVal resCycle As Integer, ByVal stepCycle As Single, ByVal isFirst As Boolean)
        '1.考虑, 日历和班次的加工情况
        If isFirst Then
            Dim resRunCycle = stepCycle \ resCycle '设备模拟运行周期取整
            If resRunCycle = 0 Then

            Else '跨天，考虑日历放假情况

                Dim offset = 0 '偏移量
                For i As Integer = 1 To resRunCycle
                    offset = GetDateOffset(2, simulateStart, stepResMKDt, 1)
                    simulateStart = simulateStart.AddDays(offset - 1)
                Next

            End If

            Dim resRunCycle2 = stepCycle Mod resCycle '设备运行周期取余数
            '追加余数时，可能会受中间休息段影响，eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
            simulateStart = GetResApsEndOrStartDate(2, simulateStart, resRunCycle2, _stepShiftDt, stepResMKDt, resourceId)
        Else '递归进来的情况
            simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

            '班次因素
            Dim resEnd = GetRunStartOrEndDate(simulateStart, resShiftEnd) '设备结束时间
            Dim resStart = GetRunStartOrEndDate(simulateStart, resShiftStart) '设备开始时间

            '------------更新班次最晚时间 add by zhangrj 20250923----------------
            Dim dtDayWorkshift = GetDayWorkShiftListByResourceID(resourceId, simulateStart) '获取设备在这一天的新的班次明细
            For Each drd As DataRow In dtDayWorkshift.Rows
                Dim rStart = GetRunStartOrEndDate(simulateEnd, drd("starttime")) '开始时间
                Dim rEnd = GetRunStartOrEndDate(simulateEnd, drd("endtime")) '结束时间
                If resStart > rStart Then '如果设备初始开始时间大于该班次的结束时间，则更新设备初始开始时间
                    resStart = rStart
                End If
                If resEnd < rEnd Then '如果设备初始结束时间小于该班次的结束时间，则更新设备初始结束时间
                    resEnd = rEnd
                End If
            Next
            '----------------------------

            If resEnd <= resStart Then 'add:Wangjh 2023/2/15
                '开始时间大于结束时间，表示跨天
                resStart = resStart.AddDays(-1)
            End If

            If simulateStart < resStart Then '例如：工序7:30开始，而设备8：30才开始
                Dim differ = GetDifferMins(simulateStart, resStart)

                simulateStart = resEnd
                simulateStart = simulateStart.AddDays(-1)
                simulateStart = simulateStart.AddDays(GetDateOffset(2, simulateStart, stepResMKDt, 0))

                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt, resourceId)
            ElseIf simulateStart > resEnd Then '例如：工序19：30开始，而设备18：30就已经结束
                Dim differ = GetDifferMins(resEnd, simulateStart)
                simulateStart = GetResApsEndOrStartDate(2, simulateStart, differ, _stepShiftDt, stepResMKDt, resourceId)
            Else '正常情况，开始时间在设备工作范围内
                '1.2考虑班次中间有休息时间段情况 eg:设备班次为8:30-12:00,13:00-16:00,16:30-17:30三个时间段
                simulateStart = GetResApsStartOrEndDate(2, simulateStart, _stepShiftDt, dtDayWorkshift)
            End If

        End If

        '2.考虑，设备保养，设备占用情况

        '注释说明{：设备模拟开始时间，}：设备模拟结束时间。[：保养或占用开始时间,]:保养或占用结束时间
        '{`````[````]````} 或者 '[````{`````]````}  情况下才会只让 开始时间 偏移

        Dim resUpKeepOffset2 As Single = 0 '保养检查后，需要偏移的分钟数，结束时间--注意需是否要计算出占用设备的实际工作时间是多少分钟?
        For i As Integer = resUpKeepDt.Rows.Count - 1 To 0 Step -1

            Dim resUpKeepRow = resUpKeepDt.Rows(i)
            Dim upkeepStart = Date.Parse(resUpKeepRow("upkeepstartdate").ToString) '保养开始时间
            Dim upkeepEnd = Date.Parse(resUpKeepRow("upkeependdate").ToString) '保养结束时间

            If simulateEnd > upkeepEnd And simulateStart < upkeepEnd Then

                If simulateStart > upkeepStart Then '[````{`````]````}                   
                    ' resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                ElseIf simulateStart < upkeepStart Then '{`````[````]````} 
                    'resUpKeepOffset2 += GetDifferMins(upkeepEnd, upkeepStart)
                    resUpKeepDt.Rows.RemoveAt(i)
                End If

                '计算实际占用分钟数，考虑班次，休息时间
                resUpKeepOffset2 += GetActualOccupyMins2(upkeepStart, upkeepEnd, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)

            End If

        Next
        simulateStart = GetResApsEndOrStartDate(2, simulateStart, resUpKeepOffset2, _stepShiftDt, stepResMKDt, resourceId)

        resUpKeepDt.AcceptChanges()

        '设备占用情况，指定设备，指定时间是否被占用，在下发的计划里查,此处逻辑可行？ 
        Dim resApsOccupyOffset2 As Single = 0 '排产占用后，需要偏移的分钟数 --注意需是计算出占用设备的实际分钟数
        For i As Integer = _apsOccupyResDT.Rows.Count - 1 To 0 Step -1

            Dim resApsRow = _apsOccupyResDT.Rows(i)
            Dim apsStartTime = Date.Parse(resApsRow("starttime").ToString)
            Dim apsEndTime = Date.Parse(resApsRow("endtime").ToString)

            If simulateEnd > apsEndTime And simulateStart < apsEndTime Then
                If simulateStart > apsStartTime Then '[````{`````]````}
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                ElseIf simulateStart < apsStartTime Then '{`````[````]````} 
                    'resApsOccupyOffset2 += GetDifferMins(apsEndTime, apsStartTime)

                    _apsOccupyResDT.Rows.RemoveAt(i)
                End If
                '计算实际占用分钟数，考虑班次，休息时间
                resApsOccupyOffset2 += GetActualOccupyMins2(apsStartTime, apsEndTime, resShiftStart, resShiftEnd, _stepShiftDt, stepResMKDt)
            End If
        Next
        simulateStart = GetResApsEndOrStartDate(2, simulateStart, resApsOccupyOffset2, _stepShiftDt, stepResMKDt, resourceId)

        _apsOccupyResDT.AcceptChanges()

        '追加分钟数后，再次依次检查 日历，班次，保养，占用 情况
        If Not (resUpKeepOffset2 = 0 And resApsOccupyOffset2 = 0) Then
            LMDescSLResStartDate(resourceId, simulateStart, simulateEnd, stepResMKDt, resUpKeepDt, _apsOccupyResDT, _stepShiftDt, resShiftStart, resShiftEnd,
                                 resCycle, stepCycle, False)
        End If

    End Sub

#End Region

#Region "有限产能，瓶颈排"
    ''' <summary>
    ''' 有限产能，瓶颈排
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMBottleneckCalculate(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False
        Dim apsType = "3"
        Dim isError = False


        Dim alreadyApsConIdList As New List(Of String)  '本排产池本次已经排产的批次id列表
        For Each r In conDt.Rows '循环批次
            Dim conBaseDt As DataTable = Aps_GetConBaseDt(r("containerid").ToString) '批次基本信息
            Dim conBaseRow = conBaseDt.Rows(0)

            'Dim faMakeCalendarDt As New DataTable '批次所在分厂的日历
            Dim faMakeCalendarDt = Aps_GetMakeCalendarDt(conBaseRow("makecalendarid").ToString)

            Dim startTime = Date.Parse(r("STARTTIME"))
            startTime = startTime.AddDays(GetDateOffset(1, startTime, faMakeCalendarDt, 0)) '检查工作日历

            Dim stepDt As DataTable = Aps_GetStepBaseDt(r("workflowid").ToString, 0, 0, 1) '工序

            '如果批次工序已经完工，筛选掉完工的工序 
            Dim conApsID = Guid.NewGuid.ToString
            Dim conApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_ConApsSelect, r("containerid").ToString, r("workflowid").ToString, "3")) '批次的计划信息
            If conApsDt.Rows.Count > 0 Then
                conApsID = conApsDt.Rows(0)("ApsPlanInfoID").ToString
            End If
            Dim stepApsDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsSelect, conApsID, r("containerid").ToString)) '批次工序的计划信息

            '工序二次筛选
            stepDt = FilterApsStep(stepDt, stepApsDt, r)

            If stepDt.Rows.Count = 0 Then
                result.Message = "批次" + r("containername").ToString + "未找到可排产的瓶颈工序"
                'Return result
                Continue For
            End If

            Dim curTime = startTime

            '先排瓶颈设备所在工序
             
            Dim stepPlanSqls As New ArrayList
            Dim bottleneckStartTime = "", bottleneckEndTime = "" '瓶颈开始和结束

            For Each stepRow As DataRow In stepDt.Rows
                
                '工序设备信息
                Dim stepReourceDt As DataTable = Aps_GetSpecResourceDt("'" + stepRow("specid").ToString + "'")
                Dim stepResourceIds As String = ""
                'Dim stepResourceIdList As New List(Of String)
                Dim resourceTable As New DataTable
                resourceTable.Columns.Add("resourceid")
                resourceTable.Columns.Add("qty")
                resourceTable.Columns("qty").DefaultValue = conBaseRow("qty")
                For Each row In stepReourceDt.Rows
                    resourceTable.Rows.Add(row("resourceid").ToString)
                    stepResourceIds += "'" + row("resourceid").ToString + "',"
                Next
                If Not String.IsNullOrWhiteSpace(stepResourceIds) Then
                    stepResourceIds = stepResourceIds.Trim(",")
                End If

                Dim _startTime = curTime '此工序的初期开始时间

                '_startTime.AddDays(GetDateOffset(1, _startTime, faMakeCalendarDt, 0)) '检查工作日历,开始时间顺移

                If stepRow("isship").ToString = "1" Then '转工序 

                ElseIf stepRow("controlcode").ToString = "LY05" Then '外委序 

                Else '本车间加工序，

                    '无设备给出提醒
                    If String.IsNullOrWhiteSpace(stepResourceIds) Then
                        result.Message = "批次" + r("containername").ToString + "工序" + stepRow("workflowstepname").ToString + "无设备"
                        Return result
                    End If
                    
                    'yuxj20221028 对本分厂工序 增加设备中人员检查
                    Dim ResourceEmpDt As DataTable = Aps_GetResEmpDt(stepResourceIds)  '此工序设备上的所有人员
                    If (ResourceEmpDt.Select("employeeid is null").Length > 0) Then
                        Dim drResourceEmp As DataRow = ResourceEmpDt.Select("employeeid is null")(0)
                        result.Message = "批次" + r("containername").ToString + "工序：" + stepRow("workflowstepname").ToString + "，设备：" + drResourceEmp("resourcename") + "——没有绑定人员"
                        Return result
                    End If
                End If

                If String.IsNullOrWhiteSpace(stepResourceIds) Then '无设备，转工或外委
                    stepResourceIds = "''"
                    resourceTable.Rows.Add("''")
                End If

                '计算工序加工周期
                GetStepCycle(conBaseRow, conDt, stepRow, resourceTable)
                
                '此工序设备当前时间前所有占用信息,排除已完工的
                Dim apsOccupyResDT = GetResOccupyDtAfterTime(stepResourceIds, alreadyApsConIdList, curTime.ToString("yyyy-MM-dd HH:mm:ss"), apsType)
                For i As Integer = apsOccupyResDT.Rows.Count - 1 To 0 Step -1
                    If m_containerObj.JudgeContainerStepIsFinish(apsOccupyResDT.Rows(i)("Containerid").ToString, apsOccupyResDT.Rows(i)("Workflowstepid").ToString).Result Then
                        apsOccupyResDT.Rows.RemoveAt(i)
                    End If
                Next
                apsOccupyResDT.AcceptChanges()

                '此工序设备的所有保养信息
                Dim stepResUpkeepDt = Aps_GetResUpKeepDtAfterTime(stepResourceIds, curTime.ToString("yyyy/MM/dd HH:mm:ss"))

                '此工序设备所有的日历信息
                Dim stepResMKDt = Aps_GetResCalendarDt(stepResourceIds)

                '此工序设备所有的班次信息
                Dim stepShiftDt = Aps_GetResWorkShiftDt(stepResourceIds)

                '设备模拟加工
                Dim resSimulationDt As DataTable = LMAscSimulationByRes(resourceTable, faMakeCalendarDt, stepResMKDt, stepShiftDt, stepResUpkeepDt, apsOccupyResDT, _startTime, curTime)

                '判断瓶颈设备的模拟开始日期是否在范围内
                If Not (Date.Parse(r("STARTTIME")) <= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString) And Date.Parse(r("ENDTIME")) >= Date.Parse(resSimulationDt.Rows(0)("simulateStart").ToString)) Then
                    result.Message += "批次:" + conBaseRow("containername").ToString + "瓶颈设备的空闲开始时间为:" + resSimulationDt.Rows(0)("simulateStart").ToString + "不在订单的计划开始和结束时间内"
                    isError = True
                    Continue For
                End If

                Dim makeEmpCounts = Val(stepRow("makeempcounts").ToString) '此工序指定的加工人数
                Dim stepEmp As DataTable = Aps_GetResEmpDt(stepResourceIds) '此工序设备上的所有人员

                Dim isMachineParallel As String = stepRow("multimachineparallel").ToString
                '设备模拟加工数据，分配人员
                Dim recResSimulationDt = LMAscSLResEmp(resSimulationDt, stepReourceDt, stepEmp, makeEmpCounts, isMachineParallel)
                'yuxj30230309多设备并行逻辑处理
                Dim strFilter = "workflowstepid='" + stepRow("workflowstepid").ToString + "'"
                Dim stepApsRows = stepApsDt.Select(strFilter)
                '为保证旧的排产明细id可以使用，此处逻辑改为如果设备个数超过工序记录数，超出的的设备记录调用插入功能
                '如果设备个数小于工序记录数，超出的的工序记录调用删除功能
                Dim delStepApsId As String = ""
                If stepApsRows.Length > recResSimulationDt.Rows.Count Then
                    '超过的Apsplandetailid记录执行删除
                    For i As Integer = recResSimulationDt.Rows.Count - 1 To stepApsRows.Length - 1
                        delStepApsId = stepApsRows(i)("Apsplandetailid").ToString
                        
                        stepPlanSqls.Add(String.Format(Sql_StepApsDel, delStepApsId))
                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, delStepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, delStepApsId))
                    Next
                End If

                For i As Integer = 0 To recResSimulationDt.Rows.Count - 1
                    Dim confirmResSL As DataRow = recResSimulationDt.Rows(i)

                    curTime = Date.Parse(confirmResSL("simulateEnd"))
                    '存入最佳设备和人员到排产计划表                    
                    '生成工序计划sql                   
                    curTime = Date.Parse(confirmResSL("simulateEnd"))

                    '存入最佳设备和人员到排产计划表
                    '生成工序计划sql
                    Dim stepApsId = ""
                    If i <= stepApsRows.Length - 1 Then
                        stepApsId = stepApsRows(0)("Apsplandetailid")
                        stepPlanSqls.Add(String.Format(Sql_StepApsUpdate, confirmResSL("simulateStart"), confirmResSL("simulateEnd"), stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty"), conApsID, stepRow("workflowstepid").ToString, stepApsId))

                        '删除apsplandetailemp
                        stepPlanSqls.Add(String.Format(Sql_StepEmpApsDel, stepApsId))
                        '删除ApsPlanDetailEmpTime
                        stepPlanSqls.Add(String.Format(Sql_EmpTimeApsDel, stepApsId))
                    Else
                        stepApsId = Guid.NewGuid.ToString()
                        stepPlanSqls.Add(String.Format(Sql_StepApsInsert, stepApsId, conApsID, r("containerid").ToString, confirmResSL("simulateStart"), confirmResSL("simulateEnd"),
                                                 stepRow("workflowstepid").ToString, stepRow("workflowstepname").ToString, "", stepRow("workcenterid"), stepRow("Teamid"),
                                                  confirmResSL("resourceid"), para("EmployeeID"), stepRow("SpecFactoryID").ToString, confirmResSL("simulateStart2"), confirmResSL("simulateEnd2"), confirmResSL("qty")))

                    End If

                    If Integer.Parse(conBaseRow("qty").ToString) = 1 Then
                        '只有一件时，不应分配到两个设备，此处取第一笔
                        Exit For
                    End If
                    'bottleneckStartTime = confirmResSL("simulateStart")
                    'bottleneckEndTime = confirmResSL("simulateEnd")
                Next
                '多条设备并行时重新赋值
                bottleneckStartTime = recResSimulationDt.Rows(0)("simulateStart")
                bottleneckEndTime = recResSimulationDt.Rows(recResSimulationDt.Rows.Count - 1)("simulateEnd")

                Exit For
            Next

            If isError = True Then
                Continue For
            End If

            '执行存入工序
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

            '查询批次开始和结束时间
            Dim conApsDt2 = DatabaseHelper.Query_ReturnDatTable(String.Format(Sql_StepApsRange, conApsID))

            '生成批次计划sql
            stepPlanSqls.Clear()
            If conApsDt.Rows.Count > 0 Then
                stepPlanSqls.Add(String.Format(Sql_ConApsUpdate, conApsDt2.Rows(0)("minstarttime"), conApsDt2.Rows(0)("maxendtime"), conApsID))
            Else
                stepPlanSqls.Add(String.Format(Sql_ConApsInsert, conApsID, 3, r("containerid").ToString, para("EmployeeID"), conApsDt2.Rows(0)("maxendtime"), "", conApsDt2.Rows(0)("minstarttime"),
                                               r("workflowid").ToString))
            End If
            '执行存入批次
            DatabaseHelper.ExecuteSqlTran(stepPlanSqls)

            '瓶颈工序排产完成，正排或倒排其他工序
            Dim DesConDt As DataTable = conDt.Clone
            DesConDt.Columns.Add("ENDSEQUENCE")
            DesConDt.ImportRow(r)
            DesConDt.Rows(0)("ENDSEQUENCE") = stepDt.Rows(0)("sequence")
            DesConDt.Rows(0)("ENDTIME") = bottleneckStartTime
            LMReverseCalculate(DesConDt, para, True)

            Dim AscConDt As DataTable = conDt.Clone
            AscConDt.Columns.Add("BEGINSEQUENCE")
            AscConDt.ImportRow(r)
            AscConDt.Rows(0)("BEGINSEQUENCE") = stepDt.Rows(0)("sequence")
            AscConDt.Rows(0)("STARTTIME") = bottleneckEndTime
            LMPositiveCalculate(AscConDt, para, True)

            '存储已经排产的批次id子串，
            If Not alreadyApsConIdList.Contains(r("containerid").ToString) And conBaseRow("containertype").ToString <> "7" Then
                alreadyApsConIdList.Add(r("containerid").ToString)
            End If
        Next

        If Not isError Then
            result.Result = True
        End If
        Return result
    End Function
#End Region

#Region "排产调整"

    ''' <summary>
    ''' 排产调整
    ''' </summary>
    ''' <param name="conDt"></param>
    ''' <param name="para"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Overrides Function LMAPSChange(ByVal conDt As DataTable, ByVal para As Dictionary(Of String, String)) As uMESProcessResult
        Dim result = New uMESProcessResult
        result.Result = False

        Dim apsType = para("ApsType")

        '工序级排产信息sql
        Dim stepUpdateSql = <string> update ApsPlanDetailpre ad set AD.Status=0,ad.FeedbackDate=null,ad.ConfirmDate=null,ad.ConfirmEmpID=null,ad.LastChangeDate=sysdate,ad.starttime=to_date('{0}','YYYY-MM-DD HH24:MI:SS'),ad.endtime=to_date('{1}','YYYY-MM-DD HH24:MI:SS'),
ad.workcenterid='{2}',ad.teamid='{3}',ad.resourceid='{4}' where ad.apsplandetailid='{5}' </string>.Value

        '工序级排产人员信息
        Dim stepEmpDel = <string>delete ApsPlanDetailemp ae where ae.apsplandetailid='{0}'</string>.Value
        Dim stepEmpSql = <string>insert into ApsPlanDetailemp (Apsplandetailid,Apsplandetailempid,Employeeid,Isuse) values ('{0}',sys_guid(),'{1}',0)</string>.Value

        '瓶颈设备和工序查询
        Dim bottleneckSql = <string>SELECT rd.resourceid,rd.resourcename,nvl(ws.shipcycle,0) shipcycle,nvl(ws.transfercycle,0) transfercycle,nvl(ws.makecycle,0) makecycle,
nvl(ws.preparecycle,0) preparecycle,nvl(ws.commicycle,0) commicycle,ws.workflowstepname,ws.workflowstepid,ws.sequence,s.specid,ws.workflowid,
decode(s.factoryid,w.factoryid,0,1) isship,nvl(ws.controlcode,s.controlcode) controlcode,nvl(ws.MakeEmpCounts,0) MakeEmpCounts,ws.workcenterid,ws.teamid,
nvl(ws.chargingqty,0) chargingqty,u.attributevalue
  from workflow w
left join workflowstep ws on ws.workflowid=w.workflowid
left join specbase sb on sb.specbaseid=ws.specbaseid
left join spec s on s.specid=nvl(sb.revofrcdid,ws.specid)
left join specresourceinfo sc on sc.specid=s.specid
left join resourcedef rd on rd.resourceid=sc.resourceid
left join userattribute u on u.parentid=w.factoryid and u.userattributename='分厂代码'
where w.workflowid='{0}'  and rd.isbottleneck=1  and rownum=1
order by ws.workflowstepid</string>.Value

        '因上一级表删除，删除apsplandetailemptime表,其他分厂此表目前无数据
        Dim stepEmpTimeSql As String =
            <string>delete from apsplandetailemptime where apsplandetailempid in (select apsplandetailempid from apsplandetailemp where apsplandetailid='{0}')</string>

        '先调整指定工序的计划，再调整其他工序计划
        Dim sqlList As New ArrayList
        sqlList.Add(String.Format(stepUpdateSql, para("StartTime"), para("EndTime"), para("WorkcenterID"), para("TeamID"), para("ResourceID"), para("ApsplanDetailID")))

        'yuxj热表增加删除apsplandetailemptime
        sqlList.Add(String.Format(stepEmpTimeSql, para("ApsplanDetailID")))
        sqlList.Add(String.Format(stepEmpDel, para("ApsplanDetailID")))

        Dim employeeIds = para("EmployeeIds")
        For Each emp As String In employeeIds.Split(",")
            sqlList.Add(String.Format(stepEmpSql, para("ApsplanDetailID"), emp))
        Next
        DatabaseHelper.ExecuteSqlTran(sqlList)

        If Not conDt.Columns.Contains("manualAdjust") Then '标记为是否手动调整后的重排
            conDt.Columns.Add("manualAdjust")
            conDt.Rows(0)("manualAdjust") = "1"
        End If

        If apsType = "1" Then '正排
            conDt.Columns.Add("BEGINSEQUENCE")
            conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
            LMPositiveCalculate(conDt, para)
        ElseIf apsType = "2" Then '倒排
            conDt.Columns.Add("ENDSEQUENCE")
            conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
            LMReverseCalculate(conDt, para)
        ElseIf apsType = "3" Then '瓶颈排
            Dim stepDt As DataTable = DatabaseHelper.Query_ReturnDatTable(String.Format(bottleneckSql, conDt.Rows(0)("workflowid").ToString)) '工序
            If stepDt.Rows.Count = 0 Then
                Return result
            End If
            Dim sequence = Val(para("Sequence"))
            If sequence > Val(stepDt.Rows(0)("sequence")) Then '顺排调整后续的
                conDt.Rows(0)("StartTime") = para("EndTime")
                conDt.Columns.Add("BEGINSEQUENCE")
                conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
                LMPositiveCalculate(conDt, para, True)
            ElseIf sequence < Val(stepDt.Rows(0)("sequence")) Then '倒排调整前序的
                conDt.Rows(0)("EndTime") = para("StartTime")
                conDt.Columns.Add("ENDSEQUENCE")
                conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
                LMReverseCalculate(conDt, para, True)
            Else '调整的就是瓶颈设备,前后都要调整
                conDt.Rows(0)("StartTime") = para("EndTime")
                conDt.Columns.Add("BEGINSEQUENCE")
                conDt.Rows(0)("BEGINSEQUENCE") = para("Sequence")
                LMPositiveCalculate(conDt, para, True)

                conDt.Columns.Remove("BEGINSEQUENCE")
                conDt.Rows(0)("EndTime") = para("StartTime")
                conDt.Columns.Add("ENDSEQUENCE")
                conDt.Rows(0)("ENDSEQUENCE") = para("Sequence")
                LMReverseCalculate(conDt, para, True)
            End If
        End If


        result.Result = True
        Return result
    End Function


#End Region
End Class
